/*
 * phc -- the open source php compiler
 * see doc/license/readme.license for licensing information
 *
 * Macro-Inline Code Generator (pronounced 'mig', like wig)
 */

#ifndef PHC_MICG_GEN
#define PHC_MICG_GEN

#include "MICG.h"

#include "lib/String.h"
#include "lib/Map.h"

#define MICG_TRUE "TRUE"
#define MICG_FALSE "FALSE"
namespace MICG
{

/* The symtable keeps track of the objects passed to a macro instantiation, and
 * their names. It handles type checks, and abstracts a number of operations
 * like lookups. */
class Symtable : public virtual GC_obj
{
	Map<string, Object*> obj_map;
	Map<string, TYPE_NAME*> type_map;

public:
	// Each of these returns a String or Node. If coerce is set, a Node will
	// be converted to a String with convert_to_string. Booleans are always
	// converted.
	Object* get_lookup (Lookup*, bool coerce = false);
	Object* get_param (Param*, bool coerce = false);

	// Check type and return an MIR::Node for the parameter NAME.
	MIR::Node* get_node (PARAM_NAME* in);

	// Get the parameter, checking its type, that it exists, etc.
	Object* get (PARAM_NAME* param, bool coerce = false);


	void add_parameter (PARAM_NAME* param_name, TYPE_NAME* type_name, Object* param);
	void check_param (PARAM_NAME* name);
};

}

class MICG_gen : public virtual GC_obj
{
public:
	// callback_t: A function which takes a string and returns a string.
	typedef string (*callback_t)(Object_list*);

	// Convert an Object to a String:
	//		Booleans are converted to MICG_TRUE/MICG_FALSE
	//		Identifiers are converted using get_value_as_string.
	//		Other Objects are converted to "true" (this allows lookups to
	//		succeed if they find anything).
	static String* convert_to_string (Object*);

	// Return a string representation of the list. This should never fail, and
	// is primarily for debugging.
	static String* to_string_rep (Object_list*);

private:
	Map<string, std::pair<callback_t, int> > callbacks;
	Map<string, MICG::Macro_list> macros;

public:
	void add_macro (MICG::Macro*);
	void add_macro_def (string str, string filename);


	string instantiate (string macro_name, Object_list* params, MICG::Node* anchor = NULL);

	// Check that the signature matches (its not called 'matches' to avoid
	// overloading the word 'match', which is generated by maketea).
	bool suitable (MICG::Macro* sig, Object_list* params);

	// Get the macro named NAME, whose rules match PARAMs.
	MICG::Macro* get_macro (string name, Object_list* params);


	// Look through PARAMS, finding any lists which are not declared with the
	// 'list' type. These are intended to be iterated through at call-time.
	// Return a list of parameter lists on which the the macro must be
	// instantiated.
	// We assume that only 1 list can be expanded per time, and this
	// will throw an error if this is violated.
	List<Object_list*>* expand_list_params (MICG::Macro* m, Object_list* params);



	// Actually substitute the parameters into the body.
	string instantiate_body (MICG::Body* body, MICG::Symtable* symtable);

	MICG::Symtable* get_symtable (MICG::Macro* macro, Object_list*);

	Object* get_expr (MICG::Expr* expr, MICG::Symtable* symtable, bool coerce = false);
	Object_list* get_expr_list (MICG::Expr_list* exprs, MICG::Symtable* symtable, bool coerce = false);


	string callback (MICG::MACRO_NAME* macro, Object_list* params);
	void register_callback (string name, callback_t callback, int param_count);

	// Wrappers around common calls. The parameters are fetched from SYMTABLE
	// using their actual parameters in MC and CB.
	String* exec (MICG::Macro_call* mc, MICG::Symtable* symtable);
	String* exec (MICG::Callback* cb, MICG::Symtable* symtable);
};



#endif // PHC_MICG_GEN
